#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
# @Time    : 2024/6/20 19:51
# @Author  : YangYong
# @File    : permission_handlers.py
# @describe: 权限处理器
"""

from abc import ABC, abstractmethod

from flask import request, Flask
from jwt import DecodeError

from apis.system_oauth.models import SystemRole, SystemUser, SystemUserRoleRelation, SystemPermission, \
    SystemRolePermissionRelation, SystemUserGroupRelation, SystemGroupPermissionRelation
from apis.system_oauth.schema.user_permission_schema import SystemUserPermissionSchema
from apis.system_oauth.schema.user_schema import SystemUserSchema
from conf.config import FlaskConfig
from public.api_exceptions.api_exceptions import JWTVerifyException
from public.api_utils.jwt_utils import JWTUtils
from public.base_model import session_scope
from public.base_response import generate_response
from public.logger import logger


class AbstractHandler(ABC):

    @abstractmethod
    def set_next(self, handler):
        pass

    @abstractmethod
    def handle_permission(self, user_id) -> bool:
        pass

    @staticmethod
    def get_role_by_user(user_id):
        with session_scope() as session:
            role = session.query(
                SystemRole
            ).join(
                SystemUserRoleRelation, SystemUserRoleRelation.role_id == SystemRole.id
            ).join(
                SystemUser, SystemUserRoleRelation.user_id == SystemUser.id
            ).filter(SystemUser.id == user_id, SystemUser.active == 1).first()

            return role.role_name if role else ''

    @staticmethod
    def get_user_role_permission(user_id):
        """获取用户权限,Obtain user permission"""
        with session_scope() as session:
            permission_info = session.query(
                SystemPermission.method,
                SystemPermission.path,
                SystemPermission.name,
                SystemUser.id.label('user_id'),
                SystemUser.username.label('username')
            ).join(
                SystemUserRoleRelation, SystemUser.id == SystemUserRoleRelation.user_id
            ).join(
                SystemRolePermissionRelation, SystemUserRoleRelation.role_id == SystemRolePermissionRelation.role_id
            ).join(
                SystemPermission, SystemRolePermissionRelation.permission_id == SystemPermission.id
            ).filter(SystemUser.id == user_id, SystemUser.active == 1).all()
            return SystemUserPermissionSchema().dump(permission_info, many=True)

    @staticmethod
    def get_user_group_permission(user_id):
        """获取用户群组权限，Obtain user group permissions"""
        with session_scope() as session:
            permission_info = session.query(
                SystemPermission.method,
                SystemPermission.path,
                SystemPermission.name,
                SystemUser.id.label('user_id'),
                SystemUser.username.label('username')
            ).join(
                SystemUserGroupRelation, SystemUser.id == SystemUserGroupRelation.user_id
            ).join(
                SystemGroupPermissionRelation,
                SystemUserGroupRelation.group_id == SystemGroupPermissionRelation.group_id
            ).join(
                SystemPermission, SystemGroupPermissionRelation.permission_id == SystemPermission.id
            ).filter(SystemUser.id == user_id, SystemUser.active == 1).all()
            return SystemUserPermissionSchema().dump(permission_info, many=True)


class PermissionHandler(AbstractHandler):
    _next_handler = None

    def set_next(self, handler):
        self._next_handler = handler
        return handler

    def handle_permission(self, user_id) -> bool:
        if self._can_handle(user_id):
            return True
        elif self._next_handler:
            return self._next_handler.handle_permission(user_id)
        else:
            return False

    @abstractmethod
    def _can_handle(self, user_id: str):
        pass


class RolePermissionHandler(PermissionHandler):
    """角色权限处理器"""

    def _can_handle(self, user_id):
        permissions = self.get_user_role_permission(user_id)
        role = self.get_role_by_user(user_id)
        if role.lower() == 'admin':
            return True
        path = request.path
        method = request.method
        for permission in permissions:
            if permission.get('path') == path and permission.get('method') == method:
                return True
        return False


class GroupPermissionHandler(PermissionHandler):
    """群组权限处理器"""

    def _can_handle(self, user_id):
        permissions = self.get_user_role_permission(user_id)
        path = request.path
        method = request.method
        for permission in permissions:
            if permission.get('path') == path and permission.get('method') == method:
                return True
        return False


class PermissionAuth:
    def __init__(self, app=None):
        if app is not None:
            self.init_app(app)
        self.black_list = []
        self.white_list = FlaskConfig.WHILE_LIST

    def init_app(self, app: Flask):
        app.before_request(self.permission_handle_entry)

    def permission_handle_entry(self):
        """权限认证的入口函数"""
        request_path = request.path
        if request_path in self.black_list:
            return generate_response(data='black list.')
        if (
                request_path not in self.white_list and
                request.method != 'OPTIONS' and
                not request.path.startswith('/swagger')
        ):
            headers = request.headers
            authorization = headers.get('Authorization')
            if authorization and authorization.startswith('Bearer '):
                token = authorization.replace('Bearer ', '')
                try:
                    payload = JWTUtils().verify_token(token)
                    user_id = self.get_user_id_by_payload(payload)
                    role_permission_handler = RolePermissionHandler()
                    group_permission_handler = GroupPermissionHandler()
                    role_permission_handler.set_next(group_permission_handler)
                    permission_denied = role_permission_handler.handle_permission(user_id)
                    if not permission_denied:
                        return generate_response(message='Permission Denied.')
                except JWTVerifyException:
                    return generate_response(code=401, message='Please login again after the token expires.')
                except DecodeError:
                    return generate_response(code=400, message='Please check the user name and password.')
            else:
                logger.error("当前用户没有token.")
                return generate_response(code=400, message='Authentication failed because the user not authenticated.')

    @staticmethod
    def get_user_id_by_payload(payload: dict):
        """
        根据payload获取用户id
        Gets the user id based on payload
        @param payload:
        @return:
        """
        login_type = payload.get('login_type')
        login_value = payload.get('login_value')
        with session_scope() as session:
            user_obj = session.query(SystemUser).filter(getattr(SystemUser, login_type) == login_value).first()
            user_info = SystemUserSchema().dump(user_obj)
            return user_info.get('id')
